package phaseI;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;

import phaseII.RegexValidator;

public class Parser {
	
	private HashMap<String, String> charClasses;
	private HashMap<String, String> identifiers;
	private RecursiveParser recursiveParser;
	final String SPECIAL_CHARS = "*+|[]()";
	
	private HashMap<String, SyntaxTree> regexTrees;

	public Parser(String regex) {
		charClasses = new HashMap<String, String>();
		regexTrees = new HashMap<String, SyntaxTree>();
		//Use recursive parser to parse Regex and convert to tree
		recursiveParser = new RecursiveParser(charClasses);
		SyntaxTree tree = recursiveParser.parse(regex);
		regexTrees = new HashMap<String, SyntaxTree>();
		if (tree != null)
			regexTrees.put("not-needed", tree);
		
		
	}
	
	/*public Parser(String filePath) {
		charClasses = new HashMap<String, String>();
		identifiers = new HashMap<String, String>();
		regexTrees = new HashMap<String, SyntaxTree>();
		
		try {
			FileInputStream fStream = new FileInputStream(filePath);
			BufferedReader buffReader = new BufferedReader(new InputStreamReader(fStream));
			
			//Parse document line by line
			boolean storingCharClass = false, storingIdentifiers = false;
			String currLine;
			while ((currLine = buffReader.readLine()) != null) {
				
				if (currLine.length() > 0 && currLine.charAt(0) == '$') {
					if (!storingCharClass && !storingIdentifiers)
						storingCharClass = true;
				}
				else {
					if (storingCharClass) {
						storingCharClass = false;
						storingIdentifiers = true;
					}
				}
				
				if (storingCharClass)
					getCharClass(currLine);
				else if (storingIdentifiers)
					getIdentifier(currLine);
			}
			
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		//Use recursive parser to parse RegExes and convert to trees
		recursiveParser = new RecursiveParser(charClasses);
		for (String identifier : identifiers.keySet()) {
			String regex = identifiers.get(identifier);
			//System.out.println("Creating tree for: "+identifier+" "+regex);
			//regexSplitter.split(identifier);
			SyntaxTree tree = recursiveParser.parse(regex);
			if (tree != null)
				regexTrees.put(identifier, tree);
		}
	}*/

	/**
	 * Parse document line for character class
	 * @param docLine - Line to parse
	 */
	private void getCharClass(String docLine) {
		String charClassLabel, charClassRegex;
		charClassLabel = getRegexLabel(docLine);
		
		if (charClassLabel != null) {
			charClassRegex = getCharRegex(docLine.substring(charClassLabel.length()));
			if (charClassRegex != null && RegexValidator.validate(charClassRegex))
				charClasses.put(charClassLabel, charClassRegex);
		}
	}

	/**
	 * Parse line for Regex label
	 * @param currLine - String to parse
	 * @return Label of the regex
	 */
	private String getRegexLabel(String line) {
		if (line == null || line.length() <= 1)
			return null;
		else {
			StringBuilder label = new StringBuilder();
			boolean formingLabel = false;
			for (int i = 0; i < line.length(); i++) {
				char c = line.charAt(i);
				
				//Start forming label when a $ character is reached
				if (c == '$') {
					label.append(c);
					formingLabel = true;
				}
				//Stop forming label when space is reached
				else if (Character.isWhitespace(c)) {
					if (formingLabel)
						break;
					else 
						continue;
				}
				else
					if (formingLabel)
						label.append(c);
			}
			
			if (label.length() < 2)
				return null;
			else
				return label.toString();
		}
	}

	private String getCharRegex(String line) {
		if (line == null || line.length() <= 2)
			return null;
		else {
			StringBuilder regex = new StringBuilder();
			//boolean formingRegex = false;
			
			for (int i = 0; i < line.length(); i++) {
				char c = line.charAt(i);
				
				if (!Character.isWhitespace(c)) {
					if ((c == 'I' || c == 'i') && (i < line.length() && (line.charAt(i+1) == 'N' || line.charAt(i) == 'n'))) {
						
						regex.append("IN");
						i += 2;
					}
					else
						regex.append(c);
				}
				else if (Character.isWhitespace(c)) {
					if (i > 0 && line.charAt(i-1) == '\\')
						regex.append(c);
				}
				
				//Start forming RegEx when a [ character is found
				/*if (c == '[') {
					regex.append(c);
					formingRegex = true;
				}
				//Stop forming RegEx when ] is reached
				else if (c == ']') {
					if (formingRegex) {
						regex.append(c);
						break;
					}
					else 
						continue;
				}
				else if (c != ' ' && formingRegex)
					regex.append(c);
				else if (c == ' ' && formingRegex)
					if (i > 0 && line.charAt(i-1) == '\\')
						regex.append(c);//line.substring(i-1, i+1));*/
				
			}
			
			//Check if we have an exception (ie: [^6] IN $DIGIT)
			/*String[] regexArr = line.split("IN");
			if (regexArr.length > 1) {
				regex.append(" IN ");
				
				String label = regexArr[1];
				String labelRegex = charClasses.get(getRegexLabel(label));
				
				if (labelRegex == null)
					return null;
				regex.append(labelRegex);
			}*/
			
			return regex.toString();
		}
	}
	
	private void getIdentifier(String docLine) {
		String regexLabel, regex;
		regexLabel = getRegexLabel(docLine);
		if (regexLabel != null) {
			regex = getIdentifierRegex(docLine.substring(regexLabel.length()));
			
			if (regex != null)
				identifiers.put(regexLabel, regex);
		}
	}
	
	private String getIdentifierRegex(String line) {
		if (line == null || line.length() <= 1)
			return null;
		else {
			StringBuilder regex = new StringBuilder();
			StringBuilder label = new StringBuilder();
			boolean formingLabel = false;
			
			for (int i = 0; i < line.length(); i++) {
				char c = line.charAt(i);
				
				if (formingLabel) {
					//Continue forming label until a special character is reached
					if (Token.isSpecialChar(c) || i == line.length()-1) {//Character.isWhitespace(c) || SPECIAL_CHARS.contains(""+c) || i == line.length()-1) {
						//Check if end of line reached
						if (i == line.length()-1 && !Token.isSpecialChar(c))
							label.append(c);
						else
							i--;
						
						//Get RegEx and append to identifier RegEx
						String labelRegex = charClasses.get(label.toString());
						
						if (labelRegex != null)
							regex.append(labelRegex);
						label = new StringBuilder();
						
						formingLabel = false;
					}
					else
						label.append(c);
				}
				else {
					if (c == '$') {
						label.append(c);
						formingLabel = true;
						continue;
					}
					else if (!Character.isWhitespace(c))
						regex.append(c);
				}
			}
		
			return regex.toString();
		}
	}

	/**
	 * Get tree representations of the regular expressions 
	 */
	public HashMap<String, SyntaxTree> getTrees() {
		return regexTrees;
	}
	
	@Override
	//Print out parse tree
	public String toString() {
		return charClasses.toString() + "\n" + identifiers.toString();
	}
}
